package com.nulldev.util.VariableAPI;

import java.awt.FontMetrics;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import javax.print.attribute.AttributeSet;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.JVM.JVM;
import com.nulldev.util.data.Arrays.arrays.FastArrayList;
import com.nulldev.util.data.Arrays.arrays.FastLists;
import com.nulldev.util.data.Arrays.maps.FastMaps;
import com.nulldev.util.data.Arrays.queues.EnhancedLinkedBlockingQueue;
import com.nulldev.util.internal.backport.concurrency9.Lists;
import com.nulldev.util.io.FastIO.FastIO;
import com.nulldev.util.languages.json.uniapi.UniJsonValue;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;

public class ArrayUtils {
	private static final Logger logger = LoggerFactory.getLogger();
	public static final String[] EMPTY_STRING_ARRAY = new String[0];
	public static final int[] EMPTY_INT_ARRAY = new int[0];
	public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];

	public static class StringArrayUtils {

		public static boolean containsString(final String[] array, final String toFind) {
			if (toFind == null || array == null || array.length == 0)
				return false;
			for (final String local : array) {
				if (local.equals(toFind)) {
					return true;
				}
			}
			return false;
		}

		public static boolean containsInString(final String[] array, final String toFind) {
			if (toFind == null || array == null || array.length == 0)
				return false;
			for (final String local : array) {
				if (local.contains(toFind)) {
					return true;
				}
			}
			return false;
		}

		public static boolean containsInStringOrStartsWith(final String[] array, final String toFind) {
			if (toFind == null || array == null || array.length == 0)
				return false;
			for (final String local : array) {
				if (local.startsWith(toFind) || local.contains(toFind)) {
					return true;
				}
			}
			return false;
		}

		public static boolean containsStartsWith(final String[] array, final String toFind) {
			if (toFind == null || array == null || array.length == 0)
				return false;
			for (final String local : array) {
				if (local.startsWith(toFind)) {
					return true;
				}
			}
			return false;
		}

		public static boolean containsStartsWithInverse(final String[] array, final String toFind) {
			if (toFind == null || array == null || array.length == 0)
				return false;
			for (final String local : array) {
				if (toFind.startsWith(local)) {
					return true;
				}
			}
			return false;
		}

		public static boolean containsInStringOrStartsWith_NoCase(final String[] array, String toFind) {
			if (toFind == null || array == null || array.length == 0)
				return false;
			toFind = toFind.toLowerCase();
			for (String local : array) {
				local = local.toLowerCase();
				if (local.startsWith(toFind) || local.contains(toFind)) {
					return true;
				}
			}
			return false;
		}

		public static boolean containsStringInArray(final List<String> array, final String toFind) {
			if (toFind == null || array == null || array.isEmpty())
				return false;
			for (final String local : array) {
				if (local.contains(toFind)) {
					return true;
				}
			}
			return false;
		}

		public static String addFrom(final String[] array, final int loc) {
			if (loc > array.length) {
				return "";
			} else if (array.length == 1) {
				return array[0];
			} else {
				final StringBuilder out = new StringBuilder();
				for (int i = loc; i < array.length; i++) {
					if (i >= array.length - 1) {
						out.append(array[i]);
					} else {
						out.append(array[i] + " ");
					}
				}
				return out.toString();
			}
		}

		public static String addFrom(final String[] array, final int loc, final char ac) {
			if (loc > array.length) {
				return "";
			}
			final StringBuilder out = new StringBuilder();
			for (int i = loc; i < array.length; i++) {
				if (i >= array.length - 1) {
					out.append(array[i]);
				} else {
					out.append(array[i] + ac);
				}
			}
			return out.toString();
		}

		public static String addFrom(final String[] array, final int loc, final String ac) {
			if (loc > array.length) {
				return "";
			}
			final StringBuilder out = new StringBuilder();
			for (int i = loc; i < array.length; i++) {
				if (i >= array.length - 1) {
					out.append(array[i]);
				} else {
					out.append(array[i] + ac);
				}
			}
			return out.toString();
		}

		public static String addFrom(final List<String> array, final int loc, final String ac) {
			final StringBuilder out = new StringBuilder();
			if (array == null || array.isEmpty() || loc > array.size() || ac == null) {
				return out.toString();
			}
			for (int i = loc; i < array.size(); i++) {
				if (i >= array.size() - 1) {
					out.append(array.get(i));
				} else {
					out.append(array.get(i) + ac);
				}
			}
			return out.toString();
		}

		public static String addTo(final String[] array, final int loc) {
			return addTo(array, loc, ' ');
		}

		public static String addTo(final String[] array, final int loc, final Character addWith) {
			final StringBuilder out = new StringBuilder();
			if (loc > array.length || loc < 0) {
				return out.toString();
			}
			for (int i = 0; i < loc; i++) {
				if (i >= array.length - 1) {
					out.append(array[i]);
				} else {
					out.append(array[i]);
					if (addWith != null)
						out.append(addWith);
				}
			}
			return out.toString();
		}

		public static String find(final String[] input, final String toFind) {
// TODO: Make me return null on invalid data or find failure!
			if (input == null || input.length == 0)
				return "";
			for (final String i : input) {
				if (i.contains(toFind)) {
					return i;
				}
			}
			return "";
		}

		public static String at(final String[] line, final int loc, final String fallback) {
			try {
				return line[loc];
			} catch (Exception e) {
				return fallback;
			}
		}

		public static String toReadableString(final String[] tags) {
			final StringBuilder s0 = new StringBuilder();
			if (tags == null || tags.length == 0)
				return s0.toString();
			for (int i = 0; i < tags.length; i++) {
				final String string = tags[i];
				if (i == tags.length - 1) {
					s0.append(string);
				} else {
					s0.append(string + ", ");
				}
			}
			return s0.toString();
		}

		public static String toString(final String[] tags) {
			final StringBuilder s0 = new StringBuilder();
			if (tags == null || tags.length == 0)
				return s0.toString();
			for (int i = 0; i < tags.length; i++) {
				final String string = tags[i];
				s0.append(string);
			}
			return s0.toString();
		}

		public static boolean cardedContains(final String[] array, String toFind) {
			if (toFind == null || array == null || array.length == 0)
				return false;
			toFind = toFind.toLowerCase();
			for (String local : array) {
				local = local.toLowerCase();
				if (local.startsWith(".*") || local.endsWith(".*")) {
					if (toFind.matches(local))
						return true;
				} else if (local.startsWith(toFind) || local.contains(toFind)) {
					return true;
				}
			}
			return false;
		}
	}

	public static boolean contains(final int[] array, final int objectToFind) {
		return indexOf(array, objectToFind) != -1;
	}

	public static int indexOf(final int[] array, final int objectToFind) {
		return indexOf(array, objectToFind, 0);
	}

	public static int indexOf(final int[] array, final int objectToFind, int startIndex) {
		if (array == null || array.length == 0) {
			return -1;
		}
		if (startIndex < 0) {
			startIndex = 0;
		}
		for (int i = startIndex; i < array.length; ++i) {
			if (objectToFind == array[i]) {
				return i;
			}
		}
		return -1;
	}

	public static <T> boolean contains(final T[] array, final T objectToFind) {
		return indexOf(array, objectToFind) != -1;
	}

	public static <T> int indexOf(final T[] array, final T objectToFind) {
		return indexOf(array, objectToFind, 0);
	}

	public static <T> int indexOf(final T[] array, final T objectToFind, int startIndex) {
		if (array == null || array.length == 0) {
			return -1;
		}
		if (startIndex < 0) {
			startIndex = 0;
		}
		if (objectToFind == null) {
			for (int i = startIndex; i < array.length; ++i) {
				if (array[i] == null) {
					return i;
				}
			}
		} else {
			for (int i = startIndex; i < array.length; ++i) {
				if (objectToFind.equals(array[i])) {
					return i;
				}
			}
		}
		return -1;
	}

	private static final boolean ENABLE_LIST_ARRAY_CONV_FAST = Arguments.notArguments("--ArrayUtils.disableFastListArrayConv");

	@SuppressWarnings(
		{ "unchecked", "unused" })
	public static <E> E[] arrayListToArray(final ArrayList<E> list) {
		if (ENABLE_LIST_ARRAY_CONV_FAST) {
			if (list == null || list.isEmpty())
				return null;
			return (E[]) list.toArray((E[]) Array.newInstance(list.get(0).getClass(), list.size()));
		} else {
			int s;
			if (list == null || (s = list.size()) < 1)
				return null;
			E[] temp;
			E typeHelper = list.get(0);

			int lastI = 0;
			try {
				Object o = Array.newInstance(typeHelper.getClass(), s);
				temp = (E[]) o;

				for (int i = 0; i < list.size(); i++) {
					lastI = i;
					if (list.get(i) != null) {
						Array.<E>set(temp, i, list.get(i));
					}
				}
			} catch (Exception e) {
				Object[] temp1 = (Object[]) new Object[list.size()];
				for (int i = 0; i < list.size(); i++) {
					try {
						temp1[i] = list.get(i);
					} catch (Exception ea) {
						// log.error("Error occured! [list=" + list + ",itemToAdd=" + list.get(i) + "]",
						// ea);
					}
				}
				return (E[]) temp1;
			}
			return temp;
		}
	}

	private static final boolean ENABLE_FAST_ADD_OPS = Arguments.notArguments("--ArrayUtils.disableFastAddOps");

	@SuppressWarnings("unchecked")
	public static <T> T[] add(final T[] items, final T item) {
		if (ENABLE_FAST_ADD_OPS) {
			if (items == null || items.length == 0)
				return (T[]) new Object[]
					{ item }; // TODO: Verify that this will work.
			final T[] t = (T[]) Array.newInstance(item.getClass(), items.length + 1); // was new Object[items.length + 1];
			System.arraycopy(items, 0, t, 0, items.length);
			t[items.length] = item;
			return t;
		} else {
			final ArrayList<T> out = new ArrayList<T>();
			for (final T it : items) {
				out.add(it);
			}
			out.add(item);
			return arrayListToArray(out);
		}
	}

	public static byte[] add(final byte[] items, final byte item) {
		if (ENABLE_FAST_ADD_OPS) {
			if (items == null || items.length == 0)
				return new byte[]
					{ item };
			final byte[] t = new byte[items.length + 1];
			System.arraycopy(items, 0, t, 0, items.length);
			t[items.length] = item;
			return t;
		} else {
			final ArrayList<Byte> out = new ArrayList<Byte>(items.length + 1);
			for (final byte it : items) {
				out.add(it);
			}
			out.add(item);
			return ByteUtil.toByte(arrayListToArray(out));
		}
	}

	public static long[] add(final long[] items, final long item) {
		if (items == null || items.length == 0)
			return new long[]
				{ item };
		final long[] t = new long[items.length + 1];
		System.arraycopy(items, 0, t, 0, items.length);
		t[items.length] = item;
		return t;
	}

	public static String[] add(final String[] items, final String item) {
		if (ENABLE_FAST_ADD_OPS) {
			if (items == null || items.length == 0)
				return new String[]
					{ item };
			final String[] t = new String[items.length + 1];
			System.arraycopy(items, 0, t, 0, items.length);
			t[items.length] = item;
			return t;
		} else {
			final ArrayList<String> out = new ArrayList<String>(items.length + 1);
			for (final String it : items) {
				out.add(it);
			}
			out.add(item);
			return arrayListToArray(out);
		}
	}

	public static <T> T[] addAll(final T[] items, final T[] items2) {
		if (items == null || items.length == 0)
			return items2;
		if (items2 == null || items2.length == 0)
			return items;
		if (ENABLE_FAST_ADD_OPS) {
			@SuppressWarnings("unchecked")
			final T[] t = (T[]) Array.newInstance(items2[0].getClass(), items.length + items2.length); // new Object[items.length + items2.length];
			System.arraycopy(items, 0, t, 0, items.length);
			System.arraycopy(items2, 0, t, items.length, items2.length);
			return t;
		} else {
			final ArrayList<T> out = new ArrayList<T>(items.length + items2.length);
			for (final T it : items) {
				out.add(it);
			}
			for (final T it : items2) {
				out.add(it);
			}
			return arrayListToArray(out);
		}
	}

	public static byte[] addAll(final byte[] items, final byte[] items2) {
		if (ENABLE_FAST_ADD_OPS) {
			final byte[] t = new byte[items.length + items2.length];
			System.arraycopy(items, 0, t, 0, items.length);
			System.arraycopy(items2, 0, t, items.length, items2.length);
			return t;
		} else {
			final ArrayList<Byte> out = new ArrayList<Byte>(items.length + items2.length);
			for (final byte it : items) {
				out.add(it);
			}
			for (final byte it : items2) {
				out.add(it);
			}
			return ByteUtil.toByte(arrayListToArray(out));
		}
	}

	public static double[] addAll(final double[] items, final double[] items2) {
		final double[] t = new double[items.length + items2.length];
		System.arraycopy(items, 0, t, 0, items.length);
		System.arraycopy(items2, 0, t, items.length, items2.length);
		return t;
	}

	public static char[] addAll(final char[] items, final char[] items2) {
		final char[] t = new char[items.length + items2.length];
		System.arraycopy(items, 0, t, 0, items.length);
		System.arraycopy(items2, 0, t, items.length, items2.length);
		return t;
	}

	public static Object[] addAll(final double[] items, final Object[] items2) {
		final Object[] t = new Object[items.length + items2.length];
		for (int i = 0; i < items.length; i++)
			t[i] = items[i];
		System.arraycopy(items2, 0, t, items.length, items2.length);
		return t;
	}

	public static void printLln(final String[] lines) {
		if (lines == null) {
			return;
		}
		for (final String s : lines) {
			FastIO.get().println(s);
		}
	}

	public static void printLln(final int[] lines) {
		if (lines == null) {
			return;
		}
		for (final int s : lines) {
			FastIO.get().println(s);
		}
	}

	public static <T> void printLln(final Iterator<T> lines) {
		if (lines == null) {
			return;
		}
		lines.forEachRemaining((s) -> FastIO.get().println(s));
	}

	public static <T> void printLln(final Iterable<T> lines) {
		if (lines == null) {
			return;
		}
		lines.forEach((s) -> FastIO.get().println(s));
	}

	public static <T> void printLln(final Enumeration<T> lines) {
		if (lines == null) {
			return;
		}
		while (lines.hasMoreElements()) {
			FastIO.get().println(lines.nextElement());
		}

	}

	public static void printLln(final AttributeSet lines) {
		if (lines == null) {
			return;
		}
		printLln(lines.toArray());
	}

	public static <T> void printLln(final Collection<T> lines) {
		if (lines == null) {
			return;
		}
		for (final T s : lines) {
			FastIO.get().println(s);
		}

	}

	public static <T> void printLln(final Set<T> lines) {
		if (lines == null) {
			return;
		}
		for (final T s : lines) {
			FastIO.get().println(s);
		}

	}

	public static <A, B> void printLln(final Map<A, B> lines) {
		if (lines == null) {
			return;
		}
		for (final Map.Entry<A, B> s : lines.entrySet()) {
			FastIO.get().println(s.getKey() + ": " + s.getValue());
		}
	}

	public static <A, B> void printLln(final Map.Entry<A, B> s) {
		if (s == null) {
			return;
		}
		FastIO.get().println(s.getKey() + ": " + s.getValue());
	}

	public static <T> void printLln(final T[] lines) {
		if (lines == null) {
			return;
		}
		for (final T s : lines) {
			System.out.println(s);
		}

	}

	public static void printLln(final int[][] lines) {
		if (lines == null) {
			return;
		}
		for (final int[] s : lines) {
			printLln(s);
		}

	}

	public static <T> void printLln(final List<T> lines) {
		if (lines == null) {
			return;
		}
		try {
			for (final T s : lines) {
				// System.out.println(s);
				FastIO.get().println(s);
			}
		} catch (Exception e) {
			logger.error("printLln -> Hit an error. [type=T_generic]", e);
		}
	}

	public static <T> void println(final T[] lines) {
		if (lines == null) {
			return;
		}
		JVM.println(Arrays.toString(lines));
	}

	public static <T> void println(final List<T> lines) {
		if (lines == null) {
			return;
		}
		JVM.println(Arrays.toString(lines.toArray()));
	}

	public static <T> void println(final Map<?, ?> lines) {
		if (lines == null) {
			return;
		}
		JVM.println(lines);
	}

	public static void println(final char[] lines) {
		if (lines == null) {
			return;
		}
		JVM.println(Arrays.toString(lines));
	}

	public static void println(final byte[] lines) {
		if (lines == null) {
			return;
		}
		JVM.println(Arrays.toString(lines));
	}

	public static void println(final int[] lines) {
		if (lines == null) {
			return;
		}
		JVM.println(Arrays.toString(lines));
	}

	public static void println(final int[][] lines) {
		if (lines == null) {
			return;
		}
		for (final int[] line : lines)
			JVM.println(Arrays.toString(line));
	}

	public static void println(final double[] lines) {
		if (lines == null) {
			return;
		}
		JVM.println(Arrays.toString(lines));
	}

	public static void println(final float[] lines) {
		if (lines == null) {
			return;
		}
		JVM.println(Arrays.toString(lines));
	}

	public static void println(final char[][] lines) {
		if (lines == null) {
			return;
		}
		for (final char[] line : lines)
			JVM.println(Arrays.toString(line));
	}

	public static void println_makechars(final int[] lines) {
		if (lines == null) {
			return;
		}
		final char[] chars = new char[lines.length];
		for (int i = 0; i < lines.length; i++) {
			chars[i] = (char) lines[i];
		}
		JVM.println(Arrays.toString(chars));
	}

	@SuppressWarnings("unchecked")
	public static <E> E[] linkedListToArray(final LinkedList<E> list) {
		if (ENABLE_LIST_ARRAY_CONV_FAST) {
			if (list == null || list.isEmpty())
				return null;
			return (E[]) list.toArray((E[]) Array.newInstance(list.get(0).getClass(), list.size()));
		} else {
			int s;
			if (list == null || (s = list.size()) < 1) {
				return null;
			}
			E[] temp;
			E typeHelper = list.get(0);
			try {
				Object o = Array.newInstance(typeHelper.getClass(), s);
				temp = (E[]) o;
				for (int i = 0; i < list.size(); i++) {
					Array.set(temp, i, list.get(i));
				}
			} catch (Exception e) {
				Object[] temp1 = (Object[]) new Object[list.size()];
				for (int i = 0; i < list.size(); i++) {
					try {
						temp1[i] = list.get(i);
					} catch (Exception ea) {
					}
				}
				return (E[]) temp1;
			}
			return temp;
		}
	}

	public static Class<?>[] classes(final Object[] inputs) {
		final LinkedList<Class<?>> classes = new LinkedList<Class<?>>();
		for (final Object object : inputs) {
			classes.add(object.getClass());
		}
		return linkedListToArray(classes);
	}

	private static final boolean ENABLE_ARRAY_OPS = Arguments.notArguments("--ArrayUtils.disableArrayOps");

	@SuppressWarnings("unchecked")
	public static <T> T[] from(final T[] data, final int from) {
		if (data == null || from < 0) {
			return (T[]) new Object[0];
		}
		try {
			if (ENABLE_ARRAY_OPS) {
				final T[] t = (T[]) Array.newInstance(data[0].getClass(), data.length - from);
				System.arraycopy(data, from, t, 0, data.length - from);
				return t;
			} else {
				final ArrayList<T> out = new ArrayList<T>();
				for (int i = from; i < data.length; i++) {
					out.add(data[i]);
				}
				return arrayListToArray(out);
			}
		} catch (Exception e) {
			JVM.errorln(e);
			return (T[]) new Object[0];
		}
	}

	@SuppressWarnings("unchecked")
	public static <T> T[] to(final T[] data, final int to) {
		if (data == null || to < 0) {
			return (T[]) new Object[0];
		}
		try {
			if (ENABLE_ARRAY_OPS) {
				final T[] t = (T[]) Array.newInstance(data[0].getClass(), data.length - to - 1);
				System.arraycopy(data, 0, t, 0, data.length - to - 1);
				return t;
			} else {
				final ArrayList<T> out = new ArrayList<T>();
				for (int i = 0; i < to; i++) {
					out.add(data[i]);
				}
				return arrayListToArray(out);
			}
		} catch (Exception e) {
			JVM.errorln(e);
			return (T[]) new Object[0];
		}
	}

	public static <T> T last(final T[] object) {
		if (object == null || object.length == 0) {
			return null;
		}
		return object[object.length - 1];
	}

	public static <T> T last(final List<T> object) {
		if (object == null || object.isEmpty()) {
			return null;
		}
		return object.get(object.size() - 1);
	}

	public static <T> T[] last(final T[] list, final int amount) {
		if (ENABLE_ARRAY_OPS) {
			@SuppressWarnings("unchecked")
			final T[] o = (T[]) Array.newInstance(list[0].getClass(), amount);
			System.arraycopy(list, list.length - amount, o, 0, amount);
			reverse(o);
			return o;
		} else {
			int i = list.length - 1;
			final ArrayList<T> why = new ArrayList<T>();
			while (i > 0) {
				why.add(list[i]);
				i -= 1;
			}
			return arrayListToArray(why);
		}
	}

	public static <T> ArrayList<T> toArrayList(final T[] list) {
		if (list == null) {
			logger.error("toArrayList(T[]) -> List given is null, returning nothing!");
			return new ArrayList<T>();
		}
		return new ArrayList<T>(Arrays.asList(list));
	}

	public static <T> ArrayList<T> toArrayList(final Iterable<T> list) {
		if (list == null) {
			logger.error("toArrayList(T[]) -> List given is null, returning nothing!");
			return new ArrayList<T>();
		}
		final ArrayList<T> x = new ArrayList<T>();
		list.forEach(x::add);
		return x;
	}

	public static <T> ArrayList<T> toArrayList(final Iterator<T> list) {
		if (list == null) {
			logger.error("toArrayList(T[]) -> List given is null, returning nothing!");
			return new ArrayList<T>();
		}
		final ArrayList<T> x = new ArrayList<T>();
		list.forEachRemaining(x::add);
		return x;
	}

	public static <T> ArrayList<T> toArrayListFast(final T[] list) {
		if (list == null) {
			logger.error("toArrayListFast(T[]) -> List given is null, returning nothing!");
			return new FastArrayList<T>(true);
		}
		return new FastArrayList<T>(Arrays.asList(list), true);
	}

	public static <T> LinkedList<T> toLinkedList(final T[] list) {
		if (list == null) {
			logger.error("toArrayList(T[]) -> List given is null, returning nothing!");
			return new LinkedList<T>();
		}
		return new LinkedList<T>(Arrays.asList(list));
	}

	public static <T> String join(final T[] array, final String joinChar) {
		final StringBuilder s = new StringBuilder();
		for (int i = 0; i < array.length; i++) {
			if (i == array.length - 1) {
				s.append(array[i]);
			} else {
				s.append(array[i] + joinChar);
			}
		}
		return s.toString();
	}

	public static <T> String join(final List<T> array, final String joinChar) {
		final StringBuilder s = new StringBuilder();
		for (int i = 0; i < array.size(); i++) {
			if (i == array.size() - 1) {
				s.append(array.get(i));
			} else {
				s.append(array.get(i) + joinChar);
			}
		}
		return s.toString();
	}

	public static <T> void reverse(T[] a) {
		final int n = a.length;
		int i;
		T t;
		for (i = 0; i < n / 2; i++) {
			t = a[i];
			a[i] = a[n - i - 1];
			a[n - i - 1] = t;
		}
	}

	public static <K, V> Map<K, V> reverse(final Map<K, V> source) {
		final Map<K, V> result = new LinkedHashMap<K, V>(source.size());
		final List<K> keys = new ArrayList<K>(source.keySet());
		Collections.reverse(keys);
		for (K key : keys) {
			result.put(key, source.get(key));
		}
		return result;
	}

	public static void reverse(int[] a) {
		final int n = a.length;
		int i;
		int t;
		for (i = 0; i < n / 2; i++) {
			t = a[i];
			a[i] = a[n - i - 1];
			a[n - i - 1] = t;
		}
	}

	public static <T> void reverse(final T[] array, final int index) {
		if (index > array.length) {
			logger.error("reverse() -> Invalid index.");
			return;
		}
		for (int i = 0; i < index / 2; i++) {
			T tempswap = array[i];
			array[i] = array[index - i - 1];
			array[index - i - 1] = tempswap;
		}
	}

	public static <T> boolean _contains(final T[] array, final T key) {
		return Arrays.asList(array).contains(key);
	}

	public static boolean _contains(final char[] array, final char key) {
		for (final char c : array) {
			if (c == key)
				return true;
		}
		return false;
	}

	public static boolean _contains(final int[] array, final int key) {
		for (final int i : array) {
			if (i == key)
				return true;
		}
		return false;
	}

	@SuppressWarnings("unchecked")
	public static <T> T[] newArray(Class<T> type, int size) {
		final Class<?> ctype = type.getComponentType();
		if (ctype == null) {
			return (T[]) type.cast(Array.newInstance(type, size));
		} else {
			return (T[]) type.cast(Array.newInstance(ctype, size));
		}
	}

	public static UniJsonValue last(final UniJsonValue array) {
		if (array == null) {
			return null;
		}
		return array.get(array.length() - 1);
	}

	public static <T> T safeGet(final List<T> list, final int index, final T fallback) {
		try {
			final T o = list.get(index);
			if (o == null) {
				return fallback;
			} else {
				return o;
			}
		} catch (Exception e) {
			return fallback;
		}
	}

	public static <T> T random(final List<T> object) {
		if (object == null)
			return null;
		return object.get(RandomUtil.randInt(0, object.size() - 1));
	}

	public static <K, V> V random(final Map<K, V> object) {
		final int k = RandomUtil.randInt(0, object.size() - 1);
		return object.get(FastLists.list(object.keySet()).get(k)); // TODO: We should use a read-only list...
	}

	public static <T> T random(final T[] object) {
		return object[RandomUtil.randInt(0, object.length - 1)];
	}

	public static char random(final char[] object) {
		return object[RandomUtil.randInt(0, object.length - 1)];
	}

	@SuppressWarnings("unchecked")
	public static <T> T[] toArray(final List<T> list) {
		return (T[]) list.toArray();
	}

	public static void main(String[] args) throws Exception {
		final ArrayList<String> o = new ArrayList<String>();
		o.add("a");
		o.add("b");
		o.add("c");
		final String[] y = arrayListToArray(o);
		JVM.println("arrayListToArray: ");
		printLln(y);
		JVM.println("to: ");
		printLln(to(y, 1));
		JVM.println("from: ");
		printLln(from(y, 1));
		JVM.println("last: ");
		printLln(last(y, 2));
	}

	public static int[] unIntArray(final Integer[] it) {
		final int[] o = new int[it.length];
		System.arraycopy(it, 0, o, 0, it.length);
		return o;
	}

	public static List<String> splitEqually(String text, int size) {
		final List<String> ret = FastLists.list((text.length() + size - 1) / size);
		for (int start = 0; start < text.length(); start += size) {
			ret.add(text.substring(start, Math.min(text.length(), start + size)));
		}
		return ret;
	}

	/**
	 * Returns an array of strings, one for each line in the string after it has
	 * been wrapped to fit lines of <var>maxWidth</var>. Lines end with any of cr,
	 * lf, or cr lf. A line ending at the end of the string will not output a
	 * further, empty string.
	 * <p>
	 * This code assumes <var>str</var> is not <code>null</code>.
	 * 
	 * @param str      the string to split
	 * @param fm       needed for string width calculations
	 * @param maxWidth the max line width, in points
	 * @return a non-empty list of strings
	 */
	public static List<String> wrap(final String str, final FontMetrics fm, final int maxWidth) {
		List<String> lines = splitIntoLines(str);
		if (lines.size() == 0)
			return lines;

		final List<String> strings = FastLists.list();
		for (final Iterator<String> iter = lines.iterator(); iter.hasNext();)
			wrapLineInto((String) iter.next(), strings, fm, maxWidth);
		return strings;
	}

	/**
	 * Given a line of text and font metrics information, wrap the line and add the
	 * new line(s) to <var>list</var>.
	 * 
	 * @param line     a line of text
	 * @param list     an output list of strings
	 * @param fm       font metrics
	 * @param maxWidth maximum width of the line(s)
	 */
	public static void wrapLineInto(String line, List<String> list, FontMetrics fm, int maxWidth) {
		int len = line.length();
		int width;
		while (len > 0 && (width = fm.stringWidth(line)) > maxWidth) {
			// Guess where to split the line. Look for the next space before
			// or after the guess.
			int guess = len * maxWidth / width;
			String before = line.substring(0, guess).trim();

			width = fm.stringWidth(before);
			int pos;
			if (width > maxWidth) // Too long
				pos = findBreakBefore(line, guess);
			else { // Too short or possibly just right
				pos = findBreakAfter(line, guess);
				if (pos != -1) { // Make sure this doesn't make us too long
					before = line.substring(0, pos).trim();
					if (fm.stringWidth(before) > maxWidth)
						pos = findBreakBefore(line, guess);
				}
			}
			if (pos == -1)
				pos = guess; // Split in the middle of the word

			list.add(line.substring(0, pos).trim());
			line = line.substring(pos).trim();
			len = line.length();
		}
		if (len > 0)
			list.add(line);
	}

	/**
	 * Returns the index of the first whitespace character or '-' in <var>line</var>
	 * that is at or before <var>start</var>. Returns -1 if no such character is
	 * found.
	 * 
	 * @param line  a string
	 * @param start where to star looking
	 */
	public static int findBreakBefore(String line, int start) {
		for (int i = start; i >= 0; --i) {
			char c = line.charAt(i);
			if (Character.isWhitespace(c) || c == '-')
				return i;
		}
		return -1;
	}

	/**
	 * Returns the index of the first whitespace character or '-' in <var>line</var>
	 * that is at or after <var>start</var>. Returns -1 if no such character is
	 * found.
	 * 
	 * @param line  a string
	 * @param start where to star looking
	 */
	public static int findBreakAfter(String line, int start) {
		int len = line.length();
		for (int i = start; i < len; ++i) {
			char c = line.charAt(i);
			if (Character.isWhitespace(c) || c == '-')
				return i;
		}
		return -1;
	}

	/**
	 * Returns an array of strings, one for each line in the string. Lines end with
	 * any of cr, lf, or cr lf. A line ending at the end of the string will not
	 * output a further, empty string.
	 * <p>
	 * This code assumes <var>str</var> is not <code>null</code>.
	 * 
	 * @param str the string to split
	 * @return a non-empty list of strings
	 */
	public static List<String> splitIntoLines(final String str) {
		final List<String> strings = FastLists.list();

		int len = str.length();
		if (len == 0) {
			strings.add("");
			return strings;
		}

		int lineStart = 0;

		for (int i = 0; i < len; ++i) {
			char c = str.charAt(i);
			if (c == '\r') {
				int newlineLength = 1;
				if ((i + 1) < len && str.charAt(i + 1) == '\n')
					newlineLength = 2;
				strings.add(str.substring(lineStart, i));
				lineStart = i + newlineLength;
				if (newlineLength == 2) // skip \n next time through loop
					++i;
			} else if (c == '\n') {
				strings.add(str.substring(lineStart, i));
				lineStart = i + 1;
			}
		}
		if (lineStart < len)
			strings.add(str.substring(lineStart));

		return strings;
	}

	@SuppressWarnings("unchecked")
	public static <T> T getOrFinal(final T[] list, final T... options) {
		for (final T x : options) {
			for (final T i : list) {
				if (i.equals(x))
					return i;
			}
		}
		return list[options.length - 1];
	}

	public static boolean isArray(final Object obj) {
		return obj instanceof Object[] || obj instanceof boolean[] || obj instanceof byte[] || obj instanceof short[] || obj instanceof char[]
				|| obj instanceof int[] || obj instanceof long[] || obj instanceof float[] || obj instanceof double[] || obj instanceof String[];
	}

	public static <T> T first(final T[] args) {
		if (args == null || args.length == 0)
			return null;
		return args[0];
	}

	@SafeVarargs
	public static <T> T[] make(final T... ts) {
		return ts;
	}

	public static <T> T[] makeFrom(final List<T> t) {
		@SuppressWarnings("unchecked")
		final T[] f = (T[]) Array.newInstance(t.get(0).getClass(), t.size());
		for (int i = 0; i < t.size(); i++) {
			f[i] = t.remove(0);
		}
		return f;
	}

	private static <T> Class<?> fromQueue(final BlockingQueue<T> f) {
		T nonNull = null;
		for (int i = 0; i < f.size(); i++) {
			final T r = f.peek();
			if (r == null) {
				f.poll();
				continue;
			} else {
				nonNull = r;
				break;
			}
		}
		return nonNull == null ? null : nonNull.getClass();
	}

	public static Object[] makeFromObject(final BlockingQueue<?> t) throws InterruptedException {
		if (t instanceof EnhancedLinkedBlockingQueue) {
			return ((EnhancedLinkedBlockingQueue<?>) t).clearToArray(new Object[t.size()]);
		} else {
			final Object[] f = new Object[t.size()];
			for (int i = 0; i < t.size(); i++) {
				final Object x = t.take();
				try {
					f[i] = x;
				} catch (Exception e) {
					logger.warn("makeFromObject(BlockingQueue): Invalid object type: " + x);
					continue;
				}
			}
			return f;
		}
//		return t.toArray(new Object[t.size()]); FIXME: I break things :( (probably since I do a copy and don't remove shit)
	}

	public static <T> T[] makeFrom(final BlockingQueue<T> t) throws InterruptedException {
		final Class<?> r = fromQueue(t);
		if (r == null)
			return null;
		@SuppressWarnings("unchecked")
		final T[] f = (T[]) Array.newInstance(r, t.size());
		for (int i = 0; i < t.size(); i++) {
			final T x = t.take();
			try {
				f[i] = x;
			} catch (Exception e) {
				logger.warn("makeFrom(BlockingQueue): Invalid object type: " + x);
				continue;
			}
		}
		return f;
	}

	public static <T> T[] makeFrom(final Queue<T> t) {
		@SuppressWarnings("unchecked")
		final T[] f = (T[]) Array.newInstance(t.peek().getClass(), t.size());
		for (int i = 0; i < t.size(); i++) {
			f[i] = t.poll();
		}
		return f;
	}

	public static int[][] splitArray(int[] arrayToSplit, int chunkSize) {
		if (chunkSize <= 0) {
			return null;
		}
		int rest = arrayToSplit.length % chunkSize;
		int chunks = arrayToSplit.length / chunkSize + (rest > 0 ? 1 : 0);
		int[][] arrays = new int[chunks][];
		for (int i = 0; i < (rest > 0 ? chunks - 1 : chunks); i++) {
			arrays[i] = Arrays.copyOfRange(arrayToSplit, i * chunkSize, i * chunkSize + chunkSize);
		}
		if (rest > 0) {
			arrays[chunks - 1] = Arrays.copyOfRange(arrayToSplit, (chunks - 1) * chunkSize, (chunks - 1) * chunkSize + rest);
		}
		return arrays;
	}

	public static Runnable[][] splitArray(final Runnable[] arrayToSplit, final int chunkSize) {
		if (chunkSize <= 0) {
			return null;
		}
		final int rest = arrayToSplit.length % chunkSize;
		final int chunks = arrayToSplit.length / chunkSize + (rest > 0 ? 1 : 0);
		final Runnable[][] arrays = new Runnable[chunks][];
		for (int i = 0; i < (rest > 0 ? chunks - 1 : chunks); i++) {
			arrays[i] = Arrays.copyOfRange(arrayToSplit, i * chunkSize, i * chunkSize + chunkSize);
		}
		if (rest > 0) {
			arrays[chunks - 1] = Arrays.copyOfRange(arrayToSplit, (chunks - 1) * chunkSize, (chunks - 1) * chunkSize + rest);
		}
		return arrays;
	}

	public static long[][] splitArray(long[] arrayToSplit, int chunkSize) {
		if (chunkSize <= 0) {
			return null;
		}
		int rest = arrayToSplit.length % chunkSize;
		int chunks = arrayToSplit.length / chunkSize + (rest > 0 ? 1 : 0);
		long[][] arrays = new long[chunks][];
		for (int i = 0; i < (rest > 0 ? chunks - 1 : chunks); i++) {
			arrays[i] = Arrays.copyOfRange(arrayToSplit, i * chunkSize, i * chunkSize + chunkSize);
		}
		if (rest > 0) {
			arrays[chunks - 1] = Arrays.copyOfRange(arrayToSplit, (chunks - 1) * chunkSize, (chunks - 1) * chunkSize + rest);
		}
		return arrays;
	}

	public static int[] merge(final int[]... intArrays) {
		return Arrays.stream(intArrays).flatMapToInt(i -> Arrays.stream(i)).toArray();
	}

	public static long[] merge(final long[]... longArrays) {
		return Arrays.stream(longArrays).flatMapToLong(i -> Arrays.stream(i)).toArray();
	}

	public static <T> List<T> cloneAndReverse(final List<T> list) {
		if (list == null || list.isEmpty())
			return Lists.of();
		final ArrayList<T> t = new ArrayList<T>(list);
		Collections.reverse(t);
		return t;
	}

	public static Collection<List<Integer>> partitionIntegerListBasedOnSize(List<Integer> inputList, int size) {
		return inputList.stream().collect(Collectors.groupingBy(s -> (s - 1) / size)).values();
	}

	public static <T> Collection<List<T>> partitionBasedOnSize(List<T> inputList, int size) {
		final AtomicInteger counter = new AtomicInteger(0);
		return inputList.stream().collect(Collectors.groupingBy(s -> counter.getAndIncrement() / size)).values();
	}

	public static <T> Collection<List<T>> partitionBasedOnCondition(List<T> inputList, Predicate<T> condition) {
		return inputList.stream().collect(Collectors.partitioningBy(s -> (condition.test(s)))).values();
	}

	public static <T, S> Collection<List<T>> splitListByFunction(final List<T> list, final Function<T, S> con) {
		final Map<Integer, List<T>> lists = FastMaps.map();
		for (final T listItem : list) {
			final S item = con.apply(listItem);
			final int hash = StringsUtil.hash8(item);
			if (!lists.containsKey(hash))
				lists.put(hash, FastLists.list());
			lists.get(hash).add(listItem);
		}
		return lists.values();
	}

	public static <T, S> Map<S, List<T>> splitListByFunctionWithHash(final List<T> list, final Function<T, S> con) {
		final Map<S, List<T>> lists = FastMaps.map();
		for (final T listItem : list) {
			final S item = con.apply(listItem);
			if (!lists.containsKey(item))
				lists.put(item, FastLists.list());
			lists.get(item).add(listItem);
		}
		return lists;
	}

	public static int findItemIndex(final String[] list, final String obj) {
		for (int i = 0; i < list.length; i++) {
			final String item = list[i];
			if (item.equals(obj))
				return i;
		}
		return -1;
	}

	public static int findItemIndexForString(final Object[] list, final String obj) {
		for (int i = 0; i < list.length; i++) {
			final Object item = list[i];
			if (item instanceof String && item.equals(obj))
				return i;
		}
		return -1;
	}

	public static <T> int findItemIndex(final T[] list, final T obj) {
		for (int i = 0; i < list.length; i++) {
			final T item = list[i];
			if (item == obj)
				return i;
		}
		return -1;
	}

	public static boolean isEmpty(final String[] list) {
		return list != null && list.length == 0;
	}

	public static boolean isNotEmpty(final String[] list) {
		return list != null && list.length > 0;
	}

	@SuppressWarnings("unchecked")
	public static <T> T[] convertTo(final Object[] arr, final Class<?> output) {
		final T[] out = (T[]) newArray(output, arr.length);
		for (int i = 0; i < out.length; i++)
			out[i] = (T) arr[i];
		return out;
	}

	/**
	 * Returns true if the two Lists are equal with respect to the objects they
	 * contain. The objects must be in the same order and be reference equal (== not
	 * .equals()).
	 */
	public static <T> boolean referenceEquals(final List<T> a, final List<T> b) {
		if (a == b) {
			return true;
		}
		final int sizeA = a.size();
		final int sizeB = b.size();
		if (a == null || b == null || sizeA != sizeB) {
			return false;
		}
		boolean diff = false;
		for (int i = 0; i < sizeA && !diff; i++) {
			diff |= a.get(i) != b.get(i);
		}
		return !diff;
	}
}
